/*-------------------------------------------------------------------------
 *
 * checksum_helper.c
 *	  使用通用例程计算各种类型的校验和
 *
 * Portions Copyright (c) 2016-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *		  src/common/checksum_helper.c
 *
 *-------------------------------------------------------------------------
 */

#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#include "common/checksum_helper.h"

/*
 * 如果“name”是一个已识别的校验和类型，将 *type 设置为相应的
 * 常量并返回 true。否则，将 *type 设置为 CHECKSUM_TYPE_NONE 并
 * 返回 false。
 */
bool pg_checksum_parse_type(char *fc_name, pg_checksum_type *fc_type)
{
	pg_checksum_type fc_result_type = CHECKSUM_TYPE_NONE;
	bool		fc_result = true;

	if (pg_strcasecmp(fc_name, "none") == 0)
		fc_result_type = CHECKSUM_TYPE_NONE;
	else if (pg_strcasecmp(fc_name, "crc32c") == 0)
		fc_result_type = CHECKSUM_TYPE_CRC32C;
	else if (pg_strcasecmp(fc_name, "sha224") == 0)
		fc_result_type = CHECKSUM_TYPE_SHA224;
	else if (pg_strcasecmp(fc_name, "sha256") == 0)
		fc_result_type = CHECKSUM_TYPE_SHA256;
	else if (pg_strcasecmp(fc_name, "sha384") == 0)
		fc_result_type = CHECKSUM_TYPE_SHA384;
	else if (pg_strcasecmp(fc_name, "sha512") == 0)
		fc_result_type = CHECKSUM_TYPE_SHA512;
	else
		fc_result = false;

	*fc_type = fc_result_type;
	return fc_result;
}

/*
 * 获取与校验和类型对应的规范人类可读名称。
 */
char * pg_checksum_type_name(pg_checksum_type fc_type)
{
	switch (fc_type)
	{
		case CHECKSUM_TYPE_NONE:
			return "NONE";
		case CHECKSUM_TYPE_CRC32C:
			return "CRC32C";
		case CHECKSUM_TYPE_SHA224:
			return "SHA224";
		case CHECKSUM_TYPE_SHA256:
			return "SHA256";
		case CHECKSUM_TYPE_SHA384:
			return "SHA384";
		case CHECKSUM_TYPE_SHA512:
			return "SHA512";
	}

	Assert(false);
	return "???";
}

/*
 * 为给定类型的校验和初始化校验和上下文。
 * 成功返回 0，失败返回 -1。
 */
int pg_checksum_init(pg_checksum_context *fc_context, pg_checksum_type fc_type)
{
	fc_context->type = fc_type;

	switch (fc_type)
	{
		case CHECKSUM_TYPE_NONE:
			/* 不执行任何操作 */
			break;
		case CHECKSUM_TYPE_CRC32C:
			INIT_CRC32C(fc_context->raw_context.c_crc32c);
			break;
		case CHECKSUM_TYPE_SHA224:
			fc_context->raw_context.c_sha2 = pg_cryptohash_create(PG_SHA224);
			if (fc_context->raw_context.c_sha2 == NULL)
				return -1;
			if (pg_cryptohash_init(fc_context->raw_context.c_sha2) < 0)
			{
				pg_cryptohash_free(fc_context->raw_context.c_sha2);
				return -1;
			}
			break;
		case CHECKSUM_TYPE_SHA256:
			fc_context->raw_context.c_sha2 = pg_cryptohash_create(PG_SHA256);
			if (fc_context->raw_context.c_sha2 == NULL)
				return -1;
			if (pg_cryptohash_init(fc_context->raw_context.c_sha2) < 0)
			{
				pg_cryptohash_free(fc_context->raw_context.c_sha2);
				return -1;
			}
			break;
		case CHECKSUM_TYPE_SHA384:
			fc_context->raw_context.c_sha2 = pg_cryptohash_create(PG_SHA384);
			if (fc_context->raw_context.c_sha2 == NULL)
				return -1;
			if (pg_cryptohash_init(fc_context->raw_context.c_sha2) < 0)
			{
				pg_cryptohash_free(fc_context->raw_context.c_sha2);
				return -1;
			}
			break;
		case CHECKSUM_TYPE_SHA512:
			fc_context->raw_context.c_sha2 = pg_cryptohash_create(PG_SHA512);
			if (fc_context->raw_context.c_sha2 == NULL)
				return -1;
			if (pg_cryptohash_init(fc_context->raw_context.c_sha2) < 0)
			{
				pg_cryptohash_free(fc_context->raw_context.c_sha2);
				return -1;
			}
			break;
	}

	return 0;
}

/*
 * 使用新数据更新校验和上下文。
 * 成功返回 0，失败返回 -1。
 */
int pg_checksum_update(pg_checksum_context *fc_context, const uint8 *fc_input,
				   size_t fc_len)
{
	switch (fc_context->type)
	{
		case CHECKSUM_TYPE_NONE:
			/* 不执行任何操作 */
			break;
		case CHECKSUM_TYPE_CRC32C:
			COMP_CRC32C(fc_context->raw_context.c_crc32c, fc_input, fc_len);
			break;
		case CHECKSUM_TYPE_SHA224:
		case CHECKSUM_TYPE_SHA256:
		case CHECKSUM_TYPE_SHA384:
		case CHECKSUM_TYPE_SHA512:
			if (pg_cryptohash_update(fc_context->raw_context.c_sha2, fc_input, fc_len) < 0)
				return -1;
			break;
	}

	return 0;
}

/*
 * 最终计算校验和并将结果写入输出缓冲区。
 *
 * 调用者必须确保缓冲区的长度至少为 PG_CHECKSUM_MAX_LENGTH
 * 字节。返回值是实际写入的字节数，或 -1 表示失败。
 */
int pg_checksum_final(pg_checksum_context *fc_context, uint8 *fc_output)
{
	int			fc_retval = 0;

	StaticAssertStmt(sizeof(pg_crc32c) <= PG_CHECKSUM_MAX_LENGTH,
					 "CRC-32C digest too big for PG_CHECKSUM_MAX_LENGTH");
	StaticAssertStmt(PG_SHA224_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
					 "SHA224 digest too big for PG_CHECKSUM_MAX_LENGTH");
	StaticAssertStmt(PG_SHA256_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
					 "SHA256 digest too big for PG_CHECKSUM_MAX_LENGTH");
	StaticAssertStmt(PG_SHA384_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
					 "SHA384 digest too big for PG_CHECKSUM_MAX_LENGTH");
	StaticAssertStmt(PG_SHA512_DIGEST_LENGTH <= PG_CHECKSUM_MAX_LENGTH,
					 "SHA512 digest too big for PG_CHECKSUM_MAX_LENGTH");

	switch (fc_context->type)
	{
		case CHECKSUM_TYPE_NONE:
			break;
		case CHECKSUM_TYPE_CRC32C:
			FIN_CRC32C(fc_context->raw_context.c_crc32c);
			fc_retval = sizeof(pg_crc32c);
			memcpy(fc_output, &fc_context->raw_context.c_crc32c, fc_retval);
			break;
		case CHECKSUM_TYPE_SHA224:
			fc_retval = PG_SHA224_DIGEST_LENGTH;
			if (pg_cryptohash_final(fc_context->raw_context.c_sha2,
									fc_output, fc_retval) < 0)
				return -1;
			pg_cryptohash_free(fc_context->raw_context.c_sha2);
			break;
		case CHECKSUM_TYPE_SHA256:
			fc_retval = PG_SHA256_DIGEST_LENGTH;
			if (pg_cryptohash_final(fc_context->raw_context.c_sha2,
									fc_output, fc_retval) < 0)
				return -1;
			pg_cryptohash_free(fc_context->raw_context.c_sha2);
			break;
		case CHECKSUM_TYPE_SHA384:
			fc_retval = PG_SHA384_DIGEST_LENGTH;
			if (pg_cryptohash_final(fc_context->raw_context.c_sha2,
									fc_output, fc_retval) < 0)
				return -1;
			pg_cryptohash_free(fc_context->raw_context.c_sha2);
			break;
		case CHECKSUM_TYPE_SHA512:
			fc_retval = PG_SHA512_DIGEST_LENGTH;
			if (pg_cryptohash_final(fc_context->raw_context.c_sha2,
									fc_output, fc_retval) < 0)
				return -1;
			pg_cryptohash_free(fc_context->raw_context.c_sha2);
			break;
	}

	Assert(fc_retval <= PG_CHECKSUM_MAX_LENGTH);
	return fc_retval;
}
